package menus.flower.recognizers;

import java.awt.geom.Point2D;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.PriorityQueue;

import menus.common.AbstractGestureAnalyzer;

import fr.lri.swingstates.gestures.Gesture;

/**
 * Class used to recognize user-drawn gestures in a Flower Menu. 
 * The method used is a k-nearest-neighbor with simple gesture data.  
 * 
 * @author mathieu
 *
 */
public class FlowerGestureRecognizer extends AbstractFlowerGestureRecognizer {
	
	
	/**
	 * Number of closest neighbors used to decide of the gesture kind
	 */
	protected int K = 5;
	
	/**
	 * Name of the file containing the gesture base
	 */
	protected String gestureBaseFileName = "../KppBaseFile.csv";
	
	/**
	 * Gesture base. A gesture is represented by :
	 * 	<ul>
	 * 		<li>An ID</li>
	 * 		<li>A start angle</li>
	 * 		<li>An end angle</li>
	 * 		<li>A rotating direction</li>
	 * 	</ul>
	 */
	protected ArrayList<double[]> gestureBase;
	
	/**
	 * true if the gesture base already have been extracted, false otherwise
	 */
	protected boolean baseExtracted = false;
	
	
	
	// ----------------------------------------------------------------- XXX

	
	public FlowerGestureRecognizer(HashSet<Integer> availableItemIDs){
		super(availableItemIDs);
	}
	
	public FlowerGestureRecognizer(HashSet<Integer> availableItemIDs, int K){
		super(availableItemIDs);
		this.K = K;
	}
	
	public FlowerGestureRecognizer(HashSet<Integer> availableItemIDs, String gestureBase){
		super(availableItemIDs);
		this.gestureBaseFileName = gestureBase;
	}
	
	public FlowerGestureRecognizer(HashSet<Integer> availableItemIDs, int K, String gestureBase){
		this(availableItemIDs, K);
		this.gestureBaseFileName = gestureBase;
	}
	
	
	
	protected double computeScore(double[] userGesture, double[] baseGesture){
		
		// Directly opposed rotating directions : can't be this gesture
		if (userGesture[ROT_DIRECTION] * baseGesture[ROT_DIRECTION] == -1)
			return 10000;
		
		return (userGesture[START_ANGLE] - baseGesture[START_ANGLE]) * (userGesture[START_ANGLE] - baseGesture[START_ANGLE])
				+ (userGesture[END_ANGLE] - baseGesture[END_ANGLE]) * (userGesture[END_ANGLE] - baseGesture[END_ANGLE]);
		
	}
	
	public int recogniseGesture(Gesture gesture){
		
		double[] gestureData = analyzeGesture(gesture);
		
		// Classifying the base gestures (low-cost operation)
		
		PriorityQueue<double[]> best = new PriorityQueue<double[]>(gestureBase.size(), new Comparator<double[]>(){
			public int compare(double[] o1, double[] o2) {
				return new Double(o1[SCORE]).compareTo(o2[SCORE]);
			}
		});
		
		for (double[] baseGesture : gestureBase){
			
			if (!availableItemIDs.contains((int)baseGesture[ID])){
				continue;
			}
			
			baseGesture[SCORE] = computeScore(gestureData, baseGesture);
			best.offer(baseGesture);
		}
		
		// Retrieving the k best ones and "voting"
		
		// Euh c'est propre ca?...
		final HashMap<Double, Integer> results = new HashMap<Double, Integer>(3);
		double id;
		
		for (int i = 0; i < K; i++){
			
			id = best.poll()[ID];
			
			if (results.get(id) == null){
				results.put(id, 1);
			}
			else {
				results.put(id, results.get(id)+1);
			}
		}
		
		return Collections.max(results.keySet(), new Comparator<Double>(){

			public int compare(Double arg0, Double arg1) {
				return results.get(arg0).compareTo(results.get(arg1));
			}
			
		}).intValue();
		
	}
	
	public void extractGestureBase(){
		
		ArrayList<double[]> result = new ArrayList<double[]>(4500); // A peu pres
		
		String ligne;
		String[] parts;
		
		try {
			BufferedReader reader = new BufferedReader(new FileReader(gestureBaseFileName));
			
			reader.readLine(); // Headers
			
			while( (ligne = reader.readLine()) != null){
				parts = ligne.split("	"); // TODO check
				
				double[] baseGesture = new double[5];
				for (int i = 0; i < 4; i++){
					baseGesture[i] = new Double(parts[7+i].replace(',', '.'));
				}
				
				result.add(baseGesture);
			}
			
			gestureBase = result;
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e){
			e.printStackTrace();
		}
		
	}
	
	public void extractGestureBase(String fileName){
		this.gestureBaseFileName = fileName;
		extractGestureBase();
	}
	
}
